﻿using OpenGL;
using OpenGL.Enums;
using OpenGL.Types;

namespace ConsoleApplication1
{
    internal sealed class CustomGLControl : OpenGLControl
    {
        private ShaderProgram shader1, shader2, shader3, shader4, shader5, currentshader;
        private uint currentShaderNo = 0;
        private float angle = 0;
        private bool showFacet = true;
        private DisplayList list1, list2, cylinder;
        private float[]
            lightDiffuse = new float[3] { 1, 1, 1 },
            lightAmbient = new float[3] { 0.15f, 0.15f, 0.15f },
            lightPosition = new float[3] { 0, 0, 1 };

        public override void Scene(int ticks)
        {
            GL.MatrixMode(MatrixMode.ModelView);
            GL.LoadIdentity();
            GL.Clear(ClearTarget.GL_COLOR_BUFFER_BIT | ClearTarget.GL_DEPTH_BUFFER_BIT);
            ShaderProgram.Empty.Use();

            GL.Light(LightName.GL_LIGHT0, LightParameter.GL_POSITION, lightPosition);
            GL.Light(LightName.GL_LIGHT0, LightParameter.GL_DIFFUSE, lightDiffuse);
            GL.Light(LightName.GL_LIGHT0, LightParameter.GL_AMBIENT, lightAmbient);

            GL.Rotate(angle, 1, 0.5, 0);
            angle += ticks / 20.0f;

            DrawObjects();

            GL.Flush();
        }
        protected override void PrepareScene()
        {
            GL.ClearColor(1f, 1f, 1f, 0.0f);
            GL.Viewport(0, 0, this.Width, this.Height);

            GL.Enable(SwitchTarget.GL_DEPTH_TEST);
            GL.Enable(SwitchTarget.GL_STENCIL_TEST);

            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Perspective(45.0, (double)Width / Height, 0.1, 100);
            GL.Translate(0, 0, -3);

            InitShaders();
            InitDisplayLists();
        }
        private void InitDisplayLists()
        {
            list2 = DisplayList.Generate();
            list2.Begin(ListMode.GL_COMPILE);
            list2.BeginSprite(BeginMode.GL_TRIANGLE_STRIP);

            list1.Color(0.5f, 0.5f, 1f);
            list1.Vertex(-0.5f, -0.5f, 0f);

            list1.Color(1, 0.3f, 1f);
            list1.Vertex(-0.5f, 0.5f, 0f);

            list1.Color(1, 0.5f, 0f);
            list1.Vertex(0.5f, 0.5f, 0f);

            list2.EndSprite();
            list2.End();


            list1 = DisplayList.Generate();
            list1.Begin(ListMode.GL_COMPILE);
            list1.BeginSprite(BeginMode.GL_TRIANGLE_STRIP);

            list1.Color(1, 0.5f, 0.3f, 1);
            list1.Vertex(-0.5f, -0.5f, 0f);

            list1.Color(1, 1, 0.3f, 1);
            list1.Vertex(-0.5f, 0.5f, 0f);

            list1.Color(0, 0.5f, 0.3f, 1);
            list1.Vertex(0.5f, 0.5f, 0f);

            list1.EndSprite();
            list1.End();

            cylinder = DisplayList.CreateCylinder(1.2f, 0.35f, 20, 30);
        }
        private void InitShaders()
        {
            shader1 = ShaderProgram.Create(Shaders.vertex_matrix, Shaders.frag_color);
            shader2 = ShaderProgram.Create(Shaders.vertex_diffuse, Shaders.frag_color);
            shader3 = ShaderProgram.Create(Shaders.vertex_normalcolor, Shaders.frag_color);
            shader4 = ShaderProgram.Create(Shaders.ToonVertexShader, Shaders.ToonFragShader);
            shader5 = ShaderProgram.Create(Shaders.SmoothLightingVertex, Shaders.SmoothLightingFragment);
            currentshader = shader5;
        }

        private void DrawObjects()
        {
            DrawCylinder();

            DrawTriangles();
        }

        private void DrawCylinder()
        {
            currentshader.Use();

            GL.Enable(SwitchTarget.GL_CULL_FACE);

            GL.PolygonMode(FaceType.GL_FRONT, PolygonMode.GL_FILL);

            GL.Color(0.5f, 0.5f, 0.5f);
            cylinder.Call();

            GL.Disable(LightName.GL_LIGHT0);
            GL.Disable(SwitchTarget.GL_LIGHTING);

            if (showFacet)
            {
                DrawFacet();    
            }            
        }

        private void DrawFacet()
        {
            shader1.Use();
            GL.Enable(SwitchTarget.GL_POLYGON_OFFSET_LINE);
            GL.PolygonOffset(-0.4f, -0.4f);
            GL.Enable(SwitchTarget.GL_BLEND);
            GL.BlendFunc(BlendMode.GL_SRC_ALPHA, BlendMode.GL_ONE_MINUS_SRC_ALPHA);
            GL.LineWidth(1f);

            GL.Color(0, 0, 0);
            GL.PolygonMode(FaceType.GL_FRONT_AND_BACK, PolygonMode.GL_LINE);
            cylinder.Call();
        }

        private void DrawTriangles()
        {
            GL.Disable(SwitchTarget.GL_BLEND);
            GL.Disable(SwitchTarget.GL_POLYGON_OFFSET_LINE);
            GL.Disable(SwitchTarget.GL_CULL_FACE);
            GL.PolygonMode(FaceType.GL_FRONT_AND_BACK, PolygonMode.GL_FILL);

            shader1.Use();
            list1.Call();

            GL.Translate(0, 0, -0.1f);
            GL.Scale(1.2d, 1.2d, 1.2d);
            GL.Rotate(30, 0, 0, 1);

            list2.Call();
        }

        internal void ChangeShader()
        {
            switch (currentShaderNo)
            {
                case 0:
                    currentshader = shader1;
                    currentShaderNo++;
                    return;
                case 1:
                    currentshader = shader2;
                    currentShaderNo ++;
                    return;
                case 2:
                    currentshader = shader3;
                    currentShaderNo++;
                    return;
                case 3:
                    currentshader = shader4;
                    currentShaderNo++;
                    return;
                case 4:
                    currentshader = shader5;
                    currentShaderNo++;
                    return;
                default:
                    currentshader = shader1;
                    currentShaderNo = 1;
                    return;
            }
        }

        internal void TriggerFacet()
        {
            showFacet = !showFacet;
        }
    }
}